#include <Python.h>
#include "structmember.h"
#include <magick/MagickCore.h>
#include <stdio.h>

static PyTypeObject SimImageType;

typedef struct {
    PyObject_HEAD
    PyObject* width; /* width */
    PyObject* height;  /* height*/
	PyObject* filename; /* file name */
	PyObject* imagename; /*image name from 8bim profile otherwise == None*/
	PyObject* imagecaption; /*image caption from 8bim profile otherwise == None*/
	Image* image;
} SimImage;

static int _RefreshSize(SimImage *self) {
	Py_XDECREF(self->width);
	self->width = PyLong_FromUnsignedLong(self->image->columns);
	if(self->width == NULL) {
		return -1;
	} 
	
	Py_XDECREF(self->height);
	self->height = PyLong_FromUnsignedLong(self->image->rows);
	if(self->height == NULL) {
		Py_DECREF(self->width);
		return -1;
	}
	
	return 0;
}

static int _GetImageNameAndCaption(SimImage *self) {
	const StringInfo * profile;
	char *attribute;
	long dataset, record, sentinel;
	register long i;
	size_t length, profile_length;
	
	profile = GetImageProfile(self->image, "8bim");

	Py_XDECREF(self->imagename);
	Py_XDECREF(self->imagecaption);
	
	if(profile == (StringInfo *) NULL) {
		self->imagename = Py_None;
		self->imagecaption = Py_None;
		return -1;
	}

	profile_length = GetStringInfoLength(profile);
	for (i=0; i < (long) profile_length; i+=(long) length)
	{
		length = 1;
		sentinel=GetStringInfoDatum(profile)[i++];
		if (sentinel != 0x1c)
			continue;
	
	 	dataset=GetStringInfoDatum(profile)[i++];
	 	record=GetStringInfoDatum(profile)[i++];
		
		attribute=(char *) NULL;
		length=(size_t) (GetStringInfoDatum(profile)[i++] << 8);
        length|=GetStringInfoDatum(profile)[i++];
		
		if(record != 5 && record != 120) {
			continue;
		}
		
		if (~length >= MaxTextExtent)
			attribute = (char *) AcquireQuantumMemory(length + MaxTextExtent, sizeof(* attribute));
		
		if (attribute != (char *) NULL)
        {
			CopyMagickString(attribute,(char *) GetStringInfoDatum(profile)+i,length+1);
			if(record == 5) {
				self->imagename = PyUnicode_FromEncodedObject(PyString_FromString(attribute), "utf-8", NULL);
			} else {
				self->imagecaption = PyUnicode_FromEncodedObject(PyString_FromString(attribute), "utf-8", NULL);
			}
			attribute = DestroyString(attribute);
		}
	}
	
	if(self->imagename == NULL) 
		self->imagename = Py_None;
	if(self->imagecaption == NULL) 
		self->imagecaption = Py_None;
		
	return 0;
}

static int SimImage_init(SimImage *self, PyObject *args, PyObject *kwds) {
    PyObject *filename = NULL;
	PyObject *tmp = NULL;
	ExceptionInfo *e;
	ImageInfo *ii;
	char *str;
	
    if (! PyArg_ParseTuple(args, "S", &filename))
        return -1; 

    tmp = self->filename;
    Py_INCREF(filename);
    self->filename = filename;
    Py_XDECREF(tmp);
		
	str = PyString_AsString(self->filename);
	if(str == NULL) {
		return -1;
	}
		
	ii = CloneImageInfo((ImageInfo *) NULL);
	strcpy(ii->filename,str);
	e = AcquireExceptionInfo();

	self->image = ReadImage(ii,e);
	DestroyImageInfo(ii);

	if(self->image == NULL) {
		PyErr_SetString(PyExc_IOError, e->reason);
		DestroyExceptionInfo(e);
		return -1;
	}
	
	DestroyExceptionInfo(e);
	_GetImageNameAndCaption(self);
	return _RefreshSize(self);
}

static void SimImage_dealloc(SimImage *self) {
    Py_XDECREF(self->filename);
	Py_XDECREF(self->width);
	Py_XDECREF(self->height);
	
	if(self->image != NULL) {
		DestroyImage(self->image);
	}
	
    self->ob_type->tp_free((PyObject*)self);
}

static PyObject* SimImage_getwidth(SimImage *self, void *closure) {
	Py_INCREF(self->width);
    return self->width;
}

static PyObject* SimImage_getimagename(SimImage *self, void *closure) {
	Py_XINCREF(self->imagename);
    return self->imagename;
}

static PyObject* SimImage_getimagecaption(SimImage *self, void *closure) {
	Py_XINCREF(self->imagecaption);
    return self->imagecaption;
}

static PyObject* SimImage_getheight(SimImage *self, void *closure) {
	Py_INCREF(self->height);
    return self->height;
}

static PyObject* SimImage_getfilename(SimImage *self, void *closure) {
	Py_INCREF(self->filename);
    return self->filename;
}

static PyObject * SimImage_save_file(SimImage *self, PyObject *args) {
	const char * filename;
	ExceptionInfo *e;
	ImageInfo *ii;
	
	if(!PyArg_ParseTuple(args,"s", &filename)) {
		return NULL;
	}
	
	ii = CloneImageInfo((ImageInfo *) NULL);
	e = AcquireExceptionInfo();
	
	if(WriteImages(ii, self->image, filename, e) == MagickFalse)
	{
		DestroyImageInfo(ii);
		PyErr_SetString(PyExc_IOError, e->reason);
		DestroyExceptionInfo(e);
		return NULL;
	}
	
	DestroyExceptionInfo(e);
	DestroyImageInfo(ii);
	Py_RETURN_TRUE;
}

Image * _ResizeImage(SimImage *self, PyObject *args) {
	int width, height;
	ExceptionInfo *e;
	Image *resized_image;
	
	if(!PyArg_ParseTuple(args,"ii", &width, &height)) {
		return (Image *) NULL;
	}

	e = AcquireExceptionInfo();
	resized_image = ResizeImage(self->image, width, height, LanczosFilter, 1.0, e);
	if(resized_image == NULL) {
		PyErr_SetString(PyExc_Exception, e->reason);
		DestroyExceptionInfo(e);
		return (Image *) NULL;
	}
	
	DestroyExceptionInfo(e);
	
	return resized_image;
}

static PyObject * SimImage_resize(SimImage *self, PyObject *args) {
	Image *resized_image;
	resized_image = _ResizeImage(self,args);
	
	if(resized_image == NULL) {
		return NULL;
	} 
	
	DestroyImage(self->image);
	self->image = resized_image;
	
	if(_RefreshSize(self) < 0) {
		Py_RETURN_FALSE;
	}
	
	Py_RETURN_TRUE;
}

static PyObject * SimImage_resize_new (SimImage *self, PyObject *args) {
	Image *resized_image;
	resized_image = _ResizeImage(self,args);
	SimImage *img;

	if(resized_image == NULL) {
		return NULL;
	} 
	
	img = (SimImage *) (&SimImageType)->tp_alloc(&SimImageType, 0);
	if(img != NULL) {
    	img->image = resized_image;
		
		if(_RefreshSize(img) < 0) {
			DestroyImage(img->image);
			Py_DECREF(img);
			return (PyObject *) NULL;
		}
	}
	
	return (PyObject *)img;
}

Image *_CropImage(SimImage *self, PyObject *args) {
	RectangleInfo rect_info;
	ExceptionInfo *e;
	Image *cropped_image;
	
	if(!PyArg_ParseTuple(args,"iiii", &(rect_info.x), &(rect_info.y), &(rect_info.width), &(rect_info.height))) {
		return (Image *) NULL;
	}
	
	e = AcquireExceptionInfo();
	cropped_image = CropImage(self->image, &rect_info, e);
	if(cropped_image == NULL) {
		PyErr_SetString(PyExc_Exception, e->reason);
		DestroyExceptionInfo(e);
		return (Image *) NULL;
	}
	
	DestroyExceptionInfo(e);
	return cropped_image;
}

static PyObject * SimImage_crop(SimImage *self, PyObject *args) {
	Image *cropped_image;
	cropped_image = _CropImage(self,args);
	
	if(cropped_image == NULL) {
		return NULL;
	} 
	
	DestroyImage(self->image);
	self->image = cropped_image;
	
	if(_RefreshSize(self) < 0) {
		Py_RETURN_FALSE;
	}
	
	Py_RETURN_TRUE;
}

static PyObject * SimImage_crop_new (SimImage *self, PyObject *args) {
	Image *cropped_image;
	cropped_image = _CropImage(self,args);
	SimImage *img;

	if(cropped_image == NULL) {
		return NULL;
	} 
	
	img = (SimImage *) (&SimImageType)->tp_alloc(&SimImageType, 0);
	if(img != NULL) {
    	img->image = cropped_image;
		
		if(_RefreshSize(img) < 0) {
			DestroyImage(img->image);
			Py_DECREF(img);
			return (PyObject *) NULL;
		}
	}
	
	return (PyObject *)img;
}

/** Members defenition **/
static PyMemberDef SimImage_members[] = {
    {NULL}  /* Sentinel */
};

/** Methods defenition **/
static PyMethodDef SimImage_methods[] = {
 	{"save_file",  (PyCFunction)SimImage_save_file, METH_VARARGS,
     "save_file(filename) -> bool\n\nSave image to a file"},
	{"resize",  (PyCFunction)SimImage_resize, METH_VARARGS,
     "resize(width,height) -> bool\n\nResize the image"},
	{"resize_new",  (PyCFunction)SimImage_resize_new, METH_VARARGS,
     "resize_new(width,height) -> image object\n\nCreate new Image object with resizing"},
	{"crop_new",  (PyCFunction)SimImage_crop_new, METH_VARARGS,
     "crop_new(x,y,width,height) -> image object\n\nCreate new Image object with cropping"},
	{"crop",  (PyCFunction)SimImage_crop, METH_VARARGS,
     "crop(x,y,width,height) -> bool\n\nCrop the image"},
 	{NULL}  /* Sentinel */
};

/** Getter and Setters defenition **/
static PyGetSetDef SimImage_getseters[] = {
    {"width", 
     (getter)SimImage_getwidth, 0,
     "Image width",
     NULL},
    {"height", 
     (getter)SimImage_getheight, 0,
     "Image height",
     NULL},
	{"filename", 
     (getter)SimImage_getfilename, 0,
     "Image filename",
     NULL},
	{"image_name", 
     (getter)SimImage_getimagename, 0,
     "Image Name from 8bim profile",
 	NULL},
	{"image_caption", 
     (getter)SimImage_getimagecaption, 0,
     "Image Caption from 8bim profile",
    NULL},
    {NULL}  /* Sentinel */
};

static PyTypeObject SimImageType = {
    PyObject_HEAD_INIT(NULL)
    0,                         /*ob_size*/
    "simplemagick.Image",      /*tp_name*/
    sizeof(SimImage),             /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    (destructor)SimImage_dealloc, /*tp_dealloc*/
    0,                         /*tp_print*/
    0,                         /*tp_getattr*/
    0,                         /*tp_setattr*/
    0,                         /*tp_compare*/
    0,                         /*tp_repr*/
    0,                         /*tp_as_number*/
    0,                         /*tp_as_sequence*/
    0,                         /*tp_as_mapping*/
    0,                         /*tp_hash */
    0,                         /*tp_call*/
    0,                         /*tp_str*/
    0,                         /*tp_getattro*/
    0,                         /*tp_setattro*/
    0,                         /*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
    "Image(filename) -> image object\n\nImage object",           /* tp_doc */
    0,		               /* tp_traverse */
    0,		               /* tp_clear */
    0,		               /* tp_richcompare */
    0,		               /* tp_weaklistoffset */
    0,		               /* tp_iter */
    0,		               /* tp_iternext */
    SimImage_methods,             /* tp_methods */
    SimImage_members,             /* tp_members */
    SimImage_getseters,           /* tp_getset */
    0,                         /* tp_base */
    0,                         /* tp_dict */
    0,                         /* tp_descr_get */
    0,                         /* tp_descr_set */
    0,                         /* tp_dictoffset */
    (initproc)SimImage_init,      /* tp_init */
    0,                         /* tp_alloc */
    0,                       /* tp_new */
};

static PyObject * simplemagick_open(PyObject* self, PyObject* args) {
 	const char* str = NULL;
	ExceptionInfo *e;
	ImageInfo *ii;
	SimImage *img;
	
	if(!PyArg_ParseTuple(args,"s", &str)) {
		return (PyObject *)NULL;
	}
	
	ii = CloneImageInfo((ImageInfo *) NULL);
	
	strcpy(ii->filename,str);
	e = AcquireExceptionInfo();
	
	img = (SimImage *) (&SimImageType)->tp_alloc(&SimImageType, 0);
	if(img != NULL) {
    	img->image = ReadImage(ii,e);
				
		if(img->image == NULL) {
			PyErr_SetString(PyExc_Exception, e->reason);
			DestroyImageInfo(ii);
			DestroyExceptionInfo(e);
			Py_DECREF(img);
			return (PyObject *)NULL;
		}

		
		
		img->width = PyLong_FromUnsignedLong(img->image->columns);
		if(img->width == NULL) {
			DestroyImageInfo(ii);
			DestroyExceptionInfo(e);
			DestroyImage(img->image);
			Py_DECREF(img);
			return (PyObject *) NULL;
		}
		
		img->height = PyLong_FromUnsignedLong(img->image->rows);
		if(img->height == NULL) {
			DestroyImageInfo(ii);
			DestroyExceptionInfo(e);
			DestroyImage(img->image);
			Py_DECREF(img->width);
			Py_DECREF(img);
			return (PyObject *) NULL;
		}
		
		img->filename = PyString_FromString(str);
		if(img->filename == NULL) {
			DestroyImageInfo(ii);
			DestroyExceptionInfo(e);
			DestroyImage(img->image);
			Py_DECREF(img->filename);
			Py_DECREF(img->width);
			Py_DECREF(img);
			return (PyObject *) NULL;
		}
	}
	
	DestroyImageInfo(ii);
	DestroyExceptionInfo(e);
	return (PyObject *)img;
}

static PyMethodDef module_methods[] = {
    {"open",  simplemagick_open, METH_VARARGS,
     "open(filename) -> image object\nOpen An Image"},
	{NULL}  /* Sentinel */
};

PyMODINIT_FUNC initsimplemagick(void) 
{
	InitializeMagick("simplemagick");

    PyObject* m;
	SimImageType.tp_new = PyType_GenericNew;
	
    if (PyType_Ready(&SimImageType) < 0)
        return;

    m = Py_InitModule3("simplemagick", module_methods,
                       "Very very very simple ImageMagick interface :)");

    if (m == NULL)
      return;

    Py_INCREF(&SimImageType);
	PyModule_AddObject(m, "Image", (PyObject *)&SimImageType);
}